# recommendation/services.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import NoSuchElementException
from recommendation.models import SolvedProblem
from django.contrib.auth.models import User
# from users.models import User as CustomUser  # 自定义的User表
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import NoSuchElementException, WebDriverException
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException, WebDriverException
import re
import requests
import json

def get_user_problem_numbers_via_json(uid):
    """
    爬取用户的已通过题目编号，基于解析 window._feInjection 的 JSON 数据。
    """
    # 断断续续研究了一段时间，找到了替换selenium爬虫的方法

    # 构建请求URL和请求头
    url = f"https://www.luogu.com.cn/user/{uid}#practice"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
    }

    try:
        # 请求页面内容
        response = requests.get(url, headers=headers)
        response.raise_for_status() # 见HTTP响应状态码

        # 提取 window._feInjection 的 JSON 数据部分
        match = re.search(r'window\._feInjection\s*=\s*JSON\.parse\(decodeURIComponent\("(.+?)"\)\);', response.text)
        if not match:
            print("未找到包含题目编号的JSON数据")
            return []

        # 提取并解析 JSON 数据
        encoded_json = match.group(1)
        decoded_json = json.loads(requests.utils.unquote(encoded_json))  # URL 解码并解析 JSON

        # 提取题目编号
        all_problem_numbers = [
            problem["pid"]
            for problem in decoded_json.get("currentData", {}).get("passedProblems", [])
        ]

        return all_problem_numbers

    except requests.exceptions.RequestException as e:
        print(f"请求失败: {str(e)}")
        return []
    except json.JSONDecodeError as e:
        print(f"JSON解析失败: {str(e)}")
        return []
    except KeyError as e:
        print(f"解析数据时发生错误: 缺少键 {str(e)}")
        return []


def detect_xpath_structure(browser):

    # 检测页面的 XPath 结构版本。
    # 根据检测到的洛谷#pratice结构的不同而返回不同的数字 1/2
    # 有div[3]的就用div3，否则用div[2]
    try:
        # 检测的是公共前缀的那个元素
        # 尝试检测新界面结构的关键元素
        if browser.find_element(By.XPATH, "//*[@id='app']/div[2]/main/div/div[2]/section[2]/div[3]"):
            return 2
    except NoSuchElementException:
        pass

    try:
        # 尝试检测老界面结构的关键元素
        if browser.find_element(By.XPATH, "//*[@id='app']/div[2]/main/div/div[2]/section[2]/div[2]"):
            return 1
    except NoSuchElementException:
        pass

    return None


def get_user_problem_numbers(uid):

    # 爬取用户的已通过题目编号。
    # 新版爬虫，添加了根据页面结构的xpath选择
    url = f"https://www.luogu.com.cn/user/{uid}#practice"
    all_problem_numbers = []

    options = Options()
    options.add_argument("--headless")
    options.add_argument("--disable-gpu")
    options.add_argument("--window-size=1920x1080")

    try:
        browser = webdriver.Chrome(options=options)
        browser.get(url)

        # 检测页面的结构版本
        structure_version = detect_xpath_structure(browser)
        if structure_version is None:
            print("未能检测到页面的结构版本")
            return []
        # print("爬虫版本是")
        # print(structure_version)
        # 根据检测到的结构版本选择合适的 XPath
        for i in range(1, 9):
            try:
                if structure_version == 1:
                    problem_collection_xpath = f"//*[@id='app']/div[2]/main/div/div[2]/section[2]/div[2]/div[{i}]/div[2]"
                else:
                    problem_collection_xpath = f"//*[@id='app']/div[2]/main/div/div[2]/section[2]/div[3]/div[{i}]/div[2]"

                problem_collection = browser.find_element(By.XPATH, problem_collection_xpath)

                j = 1
                while True:
                    try:
                        problem_xpath = f"{problem_collection_xpath}/span[{j}]/a"
                        problem_link = problem_collection.find_element(By.XPATH, problem_xpath)
                        problem_number = problem_link.get_attribute("href").split("/")[-1]
                        all_problem_numbers.append(problem_number)
                        j += 1
                    except NoSuchElementException:
                        break
            except NoSuchElementException:
                continue
    except WebDriverException as e:
        print(f"无法访问 {url}: {str(e)}")
        return []
    except Exception as e:
        print(f"发生未预期的错误: {str(e)}")
        return []
    finally:
        if 'browser' in locals():
            browser.quit()

    return all_problem_numbers

# 老版本，由于洛谷前端变更已失效(div[2]变为div[3]，同时遗留了一部分的div[3])
# def get_user_problem_numbers(uid):
#     """
#     爬取用户的已通过题目编号。
#     :param uid: 用户在OJ上的唯一ID
#     :return: 用户已通过的题目编号列表
#     """
#     url = f"https://www.luogu.com.cn/user/{uid}#practice"
#     all_problem_numbers = []
#
#     options = Options()
#     options.add_argument("--headless")
#     options.add_argument("--disable-gpu")
#     options.add_argument("--window-size=1920x1080")
#
#     try:
#         browser = webdriver.Chrome(options=options)
#         browser.get(url)
#
#         for i in range(1, 9):
#             try:
#                 problem_collection_xpath = f"//*[@id='app']/div[2]/main/div/div[2]/section[2]/div[3]/div[{i}]/div[2]"
#                 problem_collection = browser.find_element(By.XPATH, problem_collection_xpath)
#
#                 j = 1
#                 while True:
#                     try:
#                         problem_xpath = f"{problem_collection_xpath}/span[{j}]/a"
#                         problem_link = problem_collection.find_element(By.XPATH, problem_xpath)
#                         problem_number = problem_link.get_attribute("href").split("/")[-1]
#                         all_problem_numbers.append(problem_number)
#                         j += 1
#                     except NoSuchElementException:
#                         break
#             except NoSuchElementException:
#                 continue
#     except WebDriverException as e:
#         print(f"无法访问 {url}: {str(e)}")
#         return []
#     except Exception as e:
#         print(f"发生未预期的错误: {str(e)}")
#         return []
#     finally:
#         if 'browser' in locals():
#             browser.quit()
#
#     return all_problem_numbers


# 该函数已过时，新版本封装在SolvedProblem类里
# def fetch_and_store_user_problems(user):
#     """
#     根据传入的原始用户爬取数据并将其存储到数据库中
#     可用于数据初始化/更新
#     """
#     user_info = CustomUser.objects.get(username=user.username)
#     uid = user_info.luogu_uid  # 使用CustomUser模型中的luogu_uid
#     # 调用爬取功能
#     problem_numbers = get_user_problem_numbers(uid)
#
#     cnt = 0
#     for number in problem_numbers:
#         try:
#             problem_instance = Problem.objects.get(problem_number=number)
#             # 只有在 SolvedProblem 中尚不存在时，才创建新的记录
#             if not SolvedProblem.objects.filter(user=user, problem=problem_instance).exists():
#                 SolvedProblem.objects.create(user=user, problem=problem_instance)
#                 cnt += 1
#         except Problem.DoesNotExist:
#             # 如果指定的问题编号不存在，则可以在此处处理，例如记录日志或通知管理员
#             print(f"Problem with number {number} not found in the database.")
#             # 或者其他任何必要的异常处理逻辑
#     return cnt  # 返回新建的题目数量


from problems.models import Problem
from django.db.models import Q
import random


def select_random_problems(user, count):
    """为指定用户随机选择未做过的题目"""
    # 使用 updated SolvedProblem 模型，我们需要获取 Problem 的外键集合
    solved_problems = set(user.solved_problems.values_list('problem', flat=True))

    # 排除已解题目的查询需要基于问题实例进行
    eligible_problems = Problem.objects.exclude(problem_number__in=solved_problems)
    eligible_problem_numbers = list(eligible_problems.values_list('problem_number', flat=True))

    # 随机选择问题编号，返回
    return random.sample(eligible_problem_numbers, min(len(eligible_problem_numbers), count))

def clear_user_solved_problems(user):
    """
    清空指定用户的所有已解决题目记录。
    参数:
    - user: User实例，指定要清空记录的用户。
    """
    # 删除该用户的所有已解决问题记录
    deletion_count, _ = user.solved_problems.all().delete()
    # 返回删除的记录数
    return deletion_count
